package com.jielihaofeng.atguigu.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;

/**
 * @author Johnnie Wind
 * @description WebSocket Netty Server
 * @date 2021/11/24 10:59
 */
@Slf4j
public class MyServer {
    public static void main(String[] args) {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.handler(new LoggingHandler());
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();

                    // 基于 HTTP 协议，使用 HTTP 编解码器
                    pipeline.addLast(new HttpServerCodec());

                    // 是以块方式写，添加 chunkedWrite
                    pipeline.addLast(new ChunkedWriteHandler());

                    // 说明：
                    // 1. HTTP 数据在传输过程中是分段的，HttpObjectAggregator 可以将多个端聚合起来
                    // 2. 这就是为什么当浏览器发送大量数据时，就会发出多次 HTTP 请求
                    pipeline.addLast(new HttpObjectAggregator(8192));

                    // 说明：
                    // 1. websocket 的数据是以帧（frame）的形式传递的，
                    // 2. 可以看 WebSocketFrame 下有6个子类
                    // 3. 浏览器请求时，ws://localhost:7000/hello 的uri
                    // 4. WebSocketServerProtocolHandler 核心功能是将 http 协议升级为 ws 协议，保持长连接
                    // 5. 通过101来切换协议
                    pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));

                    // 自定义的 TextWebSocketFrameHandler
                    pipeline.addLast(new MyTextWebSocketFrameHandler());
                }
            });

            // 启动服务器
            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            log.error("server error", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
